package XMLScene;

import java.awt.Point;

import Tracer.*;

/**
 * Modal class of triangle object
 * 
 * @author forrest lorraine eagle
 */
public class RTTriangle implements RTIObject {
	/**
	 * the first point of triangle
	 */
	private RTVector tC0;
	/**
	 * the first vector from C0 of triangle
	 */
	private RTVector tV1;
	/**
	 * the second vector from C0 of triangle
	 */
	private RTVector tV2;

	/**
	 * surface of triangle
	 */
	private RTSurface tSurface;

	/**
	 * Constructor, taking a origo point c0, and two direction vectors c1,c2
	 * 
	 * @param c0
	 * @param c1
	 * @param c2
	 */

	public RTTriangle(final RTVector c0, final RTVector v1, final RTVector v2) {
		tC0 = c0;
		tV1 = v1;
		tV2 = v2;
		tSurface = new RTSurface(new RTColor(0, 0, 0));
	}

	/**
	 * Setter o surface object
	 * 
	 * @param surface
	 */
	public final void setSurface(final RTSurface surface) {
		tSurface = surface;
	}

	/**
	 * Getter of surface object
	 * 
	 * @return the surface of triangle
	 */
	public final RTSurface getSurface() {
		return tSurface;
	}

	/**
	 * Getter of c0
	 * 
	 * @return the first piont coordination
	 */
	public final RTVector getC0() {
		return this.tC0;
	}

	/**
	 * Getter of v1
	 */
	/**
	 * @return the first vector of triangle
	 */
	public final RTVector getV1() {
		return this.tV1;
	}

	/**
	 * Getter of v2
	 * 
	 * @return the second vector of triangle
	 */
	public final RTVector getV2() {
		return this.tV2;
	}

	/**
	 * detect the hit points for triangle object
	 * 
	 * @param ray
	 *            the ray to detect a hit point with
	 * @param hitPointObjSingleMap
	 *            the original variable to store detected result, to avoid
	 *            frequently new
	 * @return the detected result
	 */
	public final RTHitpointObjMap objectHitDetect(final RTRay ray,
			final RTHitpointObjMap hitPointObjSingleMap) {
		RTVector t0 = tC0; // Triangle origin (corner 0)
		RTVector a = tV1; // Triangle vector a
		RTVector b = tV2; // Triangle vector b

		// Compute the normal vector to the triangle's plane (Pn)
		RTVector pn = RTVector.normaliseVectors(RTVector.crossProductVectors(a,
				b));
		// Compute the distance from the origin of the coordinate system
		// (0,0,0) to the plane (D)
		double d = RTVector.dotProductVectors(t0, pn);

		// Check that the ray strikes the plane
		RTPlane thePlane = new RTPlane(pn, t0);
		RTHitpointObjMap isHit = thePlane.objectHitDetect(ray,
				hitPointObjSingleMap);
		if (isHit == null) {
			return null;
		}
		// the cos value for angle of plane normal and ray
		double angleND = RTVector.dotProductVectors(pn, ray.getDirection());
		// construct a plane R through R0 and parallel with plane, the
		// distance between R and Plane
		double distanceRP = d
				- RTVector.dotProductVectors(pn, ray.getStartPoint());
		// distance between ray origin and intersection
		double distanceOH = distanceRP / angleND;
		// Compute the vector between the origin of the triangle and the
		// intersection point
		RTVector q = new RTVector(0, 0, 0);
		if (distanceOH > 0) {
			q = RTVector.sumVectors(RTVector.sumVectors(ray.getStartPoint(),
					RTVector.multipleVectors(RTVector.normaliseVectors(ray
							.getDirection()), distanceOH)), RTVector
					.reverseVector(t0));
		}

		// The hitPoint will be in the triangle, if 0<=u<=1,0<=v<=1,u+v<=1
		double u, v;
		u = (RTVector.dotProductVectors(b, b)
				* RTVector.dotProductVectors(q, a) - RTVector
				.dotProductVectors(a, b)
				* RTVector.dotProductVectors(q, b))
				/ (RTVector.dotProductVectors(a, a)
						* RTVector.dotProductVectors(b, b) - RTVector
						.dotProductVectors(a, b)
						* RTVector.dotProductVectors(a, b));

		v = (RTVector.dotProductVectors(q, b) - u
				* RTVector.dotProductVectors(a, b))
				/ RTVector.dotProductVectors(b, b);
		if (u < 0 || u > 1) {
			return null;
		} else if (v < 0 || v > 1) {
			return null;
		} else if ((u + v) > 1) {
			return null;
		}
		hitPointObjSingleMap.setHitPoint(hitPointObjSingleMap.getHitPoint());
		hitPointObjSingleMap.setHitObject(this);
		// calculate normal vector
		hitPointObjSingleMap.setHitNormal(pn);
		// return hitPoint;
		return hitPointObjSingleMap;
	}

	/**
	 * To calculate the distance from a point to a line
	 * 
	 * @param P
	 * @param lineA
	 * @param lineB
	 * @return calculate the distance from one point to a line
	 */
	private static double calDistFromPointToLine(final RTVector p,
			final RTVector lineA, final RTVector lineB) {
		RTVector aP = RTVector.sumVectors(p, RTVector.reverseVector(lineA));
		RTVector aB = RTVector.sumVectors(lineB, RTVector.reverseVector(lineA));
		RTVector normalV = RTVector.normaliseVectors(aB);
		double f = RTVector.dotProductVectors(normalV, aP);
		double lenAP = RTVector.calVectorLength(aP);
		return Math.sqrt(lenAP * lenAP - f * f);
	}

	/**
	 * to calculate the original color of triangle object
	 * 
	 * @param validHitPointObjMap
	 *            the valid hit points
	 * @return the original color of triangle object
	 */

	public final RTColor calOriginColor(
			final RTHitpointObjMap validHitPointObjMap) {
		if (tSurface.getPigment().getColor() != null) {
			return tSurface.getPigment().getColor();
		} else {
			// PD is the distance from hit point
			// to the bottom of the triangle
			RTBitmap objectBitmap = tSurface.getPigment().getBitmap();
			double pD = RTTriangle.calDistFromPointToLine(validHitPointObjMap
					.getHitPoint(), RTVector.sumVectors(tC0, tV1), RTVector
					.sumVectors(tC0, tV2));
			// H is the height of triangle
			double h = RTTriangle.calDistFromPointToLine(tC0, RTVector
					.sumVectors(tC0, tV1), RTVector.sumVectors(tC0, tV2));
			RTVector pointM = RTVector.sumVectors(tC0, RTVector
					.multipleVectors(tV1, (h - pD) / h));
			double mN = ((h - pD) / h)
					* RTVector.calVectorLength(RTVector.sumVectors(tV1,
							RTVector.reverseVector(tV2)));
			double pM = RTVector.calVectorLength(RTVector.sumVectors(
					validHitPointObjMap.getHitPoint(), RTVector
							.reverseVector(pointM)));
			int widthOfBitmap = objectBitmap.width();
			int heightOfBitmap = objectBitmap.height();
			int positionX = (int) ((pM / mN) * widthOfBitmap - 1);
			int positionY = (int) ((pD / h) * heightOfBitmap - 1);
			if (positionX < 0) {
				positionX = 0;
			}
			if (positionY < 0) {
				positionY = 0;
			}
			// get the color of corresponding point of bitmap
			Point hitPointonBitmap = new Point();
			hitPointonBitmap.setLocation(positionX, positionY);

			return objectBitmap.getColorAtPoint(hitPointonBitmap);
		}
	}
}
